package com.knife.lov.domain.service.impl;

import com.knife.lov.infra.annotations.KnifeLovValue;
import com.knife.lov.infra.constants.CodeValueSupportType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author tianhao.luo@hand-china.com
 * @date 2021年11月14日 10:29 上午
 */
@Component
@Slf4j
public class KnifeLovValueHelper {


    private static final String SETTER = "set";
    /**
     * 第一个键为类名，第二个键为属性名
     */
    public static ThreadLocal<Map<String, Map<String, Field>>> classAllFieldCacheThreadLocal = ThreadLocal.withInitial(() -> new HashMap<>(32));
    /**
     * 第一个键为类名，第二个键为属性名
     */
    public static ThreadLocal<Map<String, Map<String, Field>>> annotationFieldCacheThreadLocal = ThreadLocal.withInitial(() -> new HashMap<>(32));
    /**
     * 第一个键为类名，第二个键为方法名
     */
    public static ThreadLocal<Map<String, Map<String, Method>>> methodCacheMapThreadLocal = ThreadLocal.withInitial(() -> new HashMap<>(32));


    public static ThreadLocal<Set<String>> scanClassCacheThreadLocal = ThreadLocal.withInitial(HashSet::new);

    /**
     * 当前线程下执行过翻译的类
     *
     * @description
     * @version 1.0
     * @author tianhao.luo@hand-china.com
     * @date 2022/2/24 10:27 上午
     * @since 1.0
     */
    public static ThreadLocal<HashSet<Integer>> currLovProcessClassThreadLocal = ThreadLocal.withInitial(HashSet::new);

    /**
     * 记录这个类是否被记录下来了
     */
    public static ThreadLocal<Map<String, Boolean>> lovProcessClassCacheThreadLocal = ThreadLocal.withInitial(() -> new HashMap<>(32));


    @Autowired
    private KnifeLovCacheAble lovCacheHelper;

    /**
     * 获取下划线命名属性名的setter方法
     *
     * @param fieldName 下划线类型属性名
     * @return setter方法名称
     */
    private static String getSetterMethodNameOfFieldName(String fieldName) {
        // 两种情况，
        // 1 第一个字符大写
        // 2 第一个字符小写
        return SETTER + Character.toUpperCase(fieldName.charAt(0)) +
                fieldName.substring(1);
    }

    /**
     * 扫描对象中添加了LovValue注解的属性，并进行缓存；暂不支持动态刷新
     *
     * @param tObj 扫描对象
     * @author tianhao.luo@hand-china.com
     * @date 2021/11/14 10:34 上午
     */
    private <T> void scanFieldFilterByAnnotation(T tObj) {
        // 如果是List类型，则对其中的元素进行扫描
        if (tObj instanceof Collection) {
            ((Collection<?>) tObj).forEach(this::scanFieldFilterByAnnotation);
        } else {
            // 记录当前对象类型是否被扫描过了；已经扫描了的，无须继续扫描
            if (tObj == null || scanClassCacheThreadLocal.get().contains(getClassName(tObj.getClass()))) {
                return;
            } else {
                scanClassCacheThreadLocal.get().add(getClassName(tObj.getClass()));
            }
        }


        Class<?> tClass = tObj.getClass();
        // 当前类对应的所有属性缓存
        Map<String, Field> allFieldMap = classAllFieldCacheThreadLocal.get().get(getClassName(tClass));
        // 当前类对应的所有注解属性缓存
        int initCapacity = 32;
        Map<String, Field> annotationFieldMap = annotationFieldCacheThreadLocal.get().getOrDefault(getClassName(tClass), new HashMap<>(initCapacity));
        // 当前类所有的方法缓存
        Map<String, Method> methodMap = methodCacheMapThreadLocal.get().getOrDefault(getClassName(tClass), new HashMap<>(initCapacity));
        methodCacheMapThreadLocal.get().put(getClassName(tClass), methodMap);
        for (Method declaredMethod : ReflectionUtils.getAllDeclaredMethods(tClass)) {
            methodMap.put(declaredMethod.getName(), declaredMethod);
        }

        // 没有就进行初始化
        if (Objects.isNull(allFieldMap)) {
            // 判断是否要跳出递归
            boolean breakFlag = true;
            Field[] declaredFields = FieldUtils.getAllFields(tClass);
            for (Field declaredField : declaredFields) {
                KnifeLovValue annotation = declaredField.getAnnotation(KnifeLovValue.class);
                if (annotation != null) {
                    breakFlag = false;
                    break;
                }
                // 未添加 lovValue 注解的属性是否要继续进行翻译
                try {
                    if (!declaredField.isAccessible()) {
                        declaredField.setAccessible(true);
                    }
                    Object o = declaredField.get(tObj);
                    if (o instanceof Collection) {
                        breakFlag = false;
                        break;
                    } else if (Objects.nonNull(o) && !BeanUtils.isSimpleProperty(o.getClass())) {
                        breakFlag = false;
                        break;
                    }
                } catch (IllegalAccessException e) {
                    log.error(e.getMessage());
                }
            }

            // 属性的普通对象类型没有被注解标记，属性也没有是集合类型的；直接退出递归
            if (breakFlag) {
                return;
            }

            allFieldMap = new HashMap<>(initCapacity);
            // 缓存起来
            classAllFieldCacheThreadLocal.get().putIfAbsent(getClassName(tClass), allFieldMap);
            annotationFieldCacheThreadLocal.get().putIfAbsent(getClassName(tClass), annotationFieldMap);
            for (Field declaredField : declaredFields) {
                allFieldMap.put(declaredField.getName(), declaredField);
                // 同时进行初始化注解缓存
                if (declaredField.getAnnotation(KnifeLovValue.class) != null) {
                    annotationFieldMap.put(declaredField.getName(), declaredField);
                }

                // 深入扫描属性
                try {
                    if (!declaredField.isAccessible()) {
                        declaredField.setAccessible(true);
                    }
                    Object o = declaredField.get(tObj);
                    if (o instanceof Collection) {
                        ((Collection<?>) o).forEach(this::scanFieldFilterByAnnotation);
                    } else {
                        // 普通
                        scanFieldFilterByAnnotation(o);
                    }
                } catch (IllegalAccessException e) {
                    log.error(e.getMessage());
                }
            }
        }

    }

    /**
     * 翻译属性值并回写
     *
     * @param sourceField 待翻译属性
     * @param t           对象
     * @author tianhao.luo@hand-china.com
     * @date 2021/11/14 11:17 下午
     */
    private <T> void translateLovField(Field sourceField, T t) {
        try {
            if (!sourceField.isAccessible()) {
                sourceField.setAccessible(true);
            }
            Object o = sourceField.get(t);
            if (Objects.isNull(o)) {
                return;
            }
            String source = o.toString();
            KnifeLovValue annotation = sourceField.getAnnotation(KnifeLovValue.class);
            String lovCode = annotation.lovCode();
            String splitSymbol = annotation.splitSymbol();
            CodeValueSupportType codeValueSupportType = annotation.supportType();
            String meaning;
            if (source.contains(splitSymbol)) {
                String[] split = source.split(splitSymbol);
                meaning = Arrays.stream(split).map(item -> selectMeaning(lovCode, item, codeValueSupportType)).collect(Collectors.joining(splitSymbol));
            } else {
                meaning = selectMeaning(lovCode, source, codeValueSupportType);
            }
            Method targetSetterMethod = getTargetSetterMethod(sourceField, t.getClass());
            if (!targetSetterMethod.isAccessible()) {
                targetSetterMethod.setAccessible(true);
            }
            targetSetterMethod.invoke(t, meaning);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

    /**
     * 执行自定义翻译操作
     *
     * @param lovCode              lov编码
     * @param source               待翻译的值
     * @param codeValueSupportType 取值集中维护的哪个字段
     * @return 翻译后的结果值
     * @author tianhao.luo@hand-china.com
     * @date 2021/11/14 11:17 下午
     */
    public String selectMeaning(String lovCode, String source, CodeValueSupportType codeValueSupportType) {
        return lovCacheHelper.selectMeaning(lovCode, source, codeValueSupportType);
    }

    /**
     * @param tClass 类对象
     * @return 固定格式的字符串，便于做缓存
     * @author tianhao.luo@hand-china.com
     * @date 2021/11/14 11:18 下午
     */
    private <T> String getClassName(Class<T> tClass) {
        return tClass.getTypeName();
    }

    /**
     * 获取待翻译字段指定字段的setter方法
     *
     * @param sourceField 原属性
     * @param tClass      当前对象类型
     * @return setter方法
     * @author tianhao.luo@hand-china.com
     * @date 2021/11/14 11:19 下午
     */
    private <T> Method getTargetSetterMethod(Field sourceField, Class<T> tClass) {
        KnifeLovValue lovAnnotation = sourceField.getAnnotation(KnifeLovValue.class);
        String meaningField = lovAnnotation.meaningField();
        // 获取目标方法名
        Map<String, Method> methodMap = methodCacheMapThreadLocal.get().get(getClassName(tClass));
        return methodMap.get(getSetterMethodNameOfFieldName(meaningField));
    }


    /**
     * lov翻译操作
     *
     * @param sourceObj 原对象
     * @author tianhao.luo@hand-china.com
     * @date 2021/11/14 11:19 下午
     */
    private <T> void lovProcessTranslate(T sourceObj) {
        // 记录下当前线程已经扫描过的，避免死循环
        if (currLovProcessClassThreadLocal.get().contains(sourceObj.hashCode())) {
            return;
        } else {
            currLovProcessClassThreadLocal.get().add(sourceObj.hashCode());
        }

        // 扫描
        scanFieldFilterByAnnotation(sourceObj);
        if (sourceObj instanceof Collection) {
            ((Collection<?>) sourceObj).forEach(this::lovProcessTranslate);
        }
        Map<String, Field> annotationMap = annotationFieldCacheThreadLocal.get().get(getClassName(sourceObj.getClass()));

        // 继续向下找，对每一个属性类型的所有属性进行递归翻译；如果是集合类型，则针对集合内属性类型进行翻译
        Map<String, Field> allFieldMap = KnifeLovValueHelper.classAllFieldCacheThreadLocal.get().getOrDefault(getClassName(sourceObj.getClass()), new HashMap<>(1));

        // 定义退出的时机,没有需要翻译的集合并且没有集合类型的元素
        Boolean aBoolean = lovProcessClassCacheThreadLocal.get().get(getClassName(sourceObj.getClass()));
        if (aBoolean == null) {
            boolean b = allFieldMap.values().stream().anyMatch(field -> {
                Object o = null;
                try {
                    if (!field.isAccessible()) {
                        field.setAccessible(true);
                    }
                    o = field.get(sourceObj);
                } catch (IllegalAccessException e) {
                    log.error(e.getMessage());
                }
                return o instanceof Collection || (o != null && !BeanUtils.isSimpleProperty(o.getClass()));
            });
            aBoolean = !CollectionUtils.isEmpty(annotationMap) || b;
            lovProcessClassCacheThreadLocal.get().put(getClassName(sourceObj.getClass()), aBoolean);
        }

        if (!aBoolean) {
            return;
        }

        // 对所有的注解属性进行翻译
        annotationMap.values().forEach(field -> translateLovField(field, sourceObj));
        allFieldMap.values().forEach(field -> {
            // 非注解标记属性进行匹配
            if (!annotationMap.containsKey(field.getName())) {
                try {
                    if (!field.isAccessible()) {
                        field.setAccessible(true);
                    }
                    Object fieldObj = field.get(sourceObj);
                    // 普通属性已经进行了值集翻译，此处只需要针对集合属性进行翻译
                    if (Objects.nonNull(fieldObj) && fieldObj instanceof Collection) {
                        // 集合则针对其中每一个元素对象进行翻译
                        ((Collection<?>) fieldObj).forEach(this::lovProcessTranslate);
                    } else if (Objects.nonNull(fieldObj) && !BeanUtils.isSimpleProperty(fieldObj.getClass())) {
                        lovProcessTranslate(fieldObj);
                    }
                } catch (IllegalAccessException e) {
                    log.error(e.getMessage());
                }
            }
        });
    }

    /**
     * 该方法于2022-02-24被废弃掉，选用使用spring 提供的方法{@link BeanUtils#isSimpleProperty(Class)}实现
     *
     * @param object 待判断对象
     * @return 是否为基础类型或包装类型
     * @description
     * @version 1.0
     * @author tianhao.luo@hand-china.com
     * @date 2022/1/26 7:07 下午
     * @since 1.0
     */
    @Deprecated
    private boolean isPrimitiveOrPackage(Object object) {
        if (Objects.isNull(object)) {
            return false;
        } else if (object instanceof Date) {
            return true;
        } else if (object instanceof Boolean) {
            return true;
        } else if (object instanceof String) {
            return true;
        } else if (object instanceof Number) {
            return true;
        } else if (object.getClass().isPrimitive()) {
            return true;
        } else {
            return false;
        }
    }


    /**
     * 手动翻译对象中的值集时，清除缓存
     *
     * @param sourceObj 翻译值集
     * @description
     * @version 1.0
     * @author tianhao.luo@hand-china.com
     * @date 2021/12/15 7:36 下午
     * @since 1.0
     */
    public <T> void lovProcessTranslateWithClearCache(T sourceObj) {
        try {
            log.debug("进行值集翻译");
            lovProcessTranslate(sourceObj);
        } finally {
            // 清理全局缓存(系统存在长时间运行之后,无法进行翻译的情况,可能是其中的全局缓存被驱逐了,所以这里改为每次翻译完清理)
            scanClassCacheThreadLocal.remove();
            lovProcessClassCacheThreadLocal.remove();
            classAllFieldCacheThreadLocal.remove();
            annotationFieldCacheThreadLocal.remove();
            methodCacheMapThreadLocal.remove();
            lovCacheHelper.clear();
            currLovProcessClassThreadLocal.remove();
            log.debug("值集翻译本地缓存已清除");
        }
    }
}
